#ifndef _MI_SERVICE_CONNECTOR_H_
#define _MI_SERVICE_CONNECTOR_H_

#include "listeningObject.h"
#include "eventHandler.h"
#include "MI_client.h"
#include "Thread.h"

class MIServiceConnector; //definicion adelantada

/**
* Hebra "policia". Se encarga de tomar los usuarios no actualizados y si han
* pasado un tiempo demasiado grande sin actualizarse, entonces son desconectados
*
* @author Carlos Rodriguez Dominguez
* @date 2/12/2008
*/
class PoliceThread : public auxiliary::Thread{
private:
	MIServiceConnector* ref; /**< Referencia a la instancia de MIServiceConnector */

public:
	/**
	* Constructor
	*
	* @param r Referencia a la instancia de MIServiceConnector
	*/	
	PoliceThread(MIServiceConnector* r);
	
	/**
	* Reimplementacion del metodo "run" de la
	* superclase.
	*/
	void run();
};

/**
* Hebra encargada de enviar eventos indicando que el usuario actual
* esta conectado (actualiza su marca de tiempo en el sistema).
*
* @author Carlos Rodriguez Dominguez
* @date 2/12/2008
*/
class KeepAliveThread : public auxiliary::Thread{
private:
	MIServiceConnector* ref; /**< Referencia a la instancia de MIServiceConnector */
	
public:
	/**
	* Constructor
	*
	* @param r Referencia a la instancia de MIServiceConnector
	*/
	KeepAliveThread(MIServiceConnector* r);
	
	/**
	* Reimplementacion del metodo "run" de la
	* superclase.
	*/
	void run();
};

/**
* @brief Clase que implementa un conector para el servicio de metainformacion
* @author Carlos Rodriguez Dominguez
* @date 23/11/2008
*
* Implementacion de un conector para el servicio de metainformacion. Se encarga
* del envio automatico de eventos y del acceso a los datos del servicio de
* metainformacion. Previamente al uso de esta clase debera crearse una instancia
* de @see EventHandler y de @see MI_client. Con MI_client se debera escoger una
* aplicacion y un usuario valido para esta aplicacion.
*/
class MIServiceConnector : public ListeningObject {
private:	
	friend class KeepAliveThread; //para poder acceder a los datos miembro privados
	friend class PoliceThread; //para poder acceder a los datos miembro privados
	
	char* user; /**< Usuario */
	char* role; /**< Rol actual del usuario */
	char* password; /**< Clave asociada al usuario */
	char* application; /**< Nombre de la aplicacion */
	bool login_allowed; /**< Para comprobar si se permite conectar al usuario actual */
	bool connected; /**< Comprueba si el usuario esta conectado */
	EventHandler* handler; /**< Manejador de eventos asociado al conector */
	MI_client* mi_client; /**< Cliente para el servicio de metainformacion */
	KeepAliveThread* keep_alive_thread; /**< Hebra que actualiza la marca de tiempo del usuario */
	PoliceThread* police_thread; /**< Hebra que desconecta los usuarios no actualizados */
	
public:
	/**
	* Constructor
	*
	* @param app Aplicacion
	* @param u Usuario
	* @param pass Clave
	* @param h Manejador de eventos que se asociara al conector
	* @param mc Cliente de metainformacion para el servicio
	*/
	MIServiceConnector(const char* app, const char* u, const char* pass, 
						EventHandler* h, MI_client* mc);
	
	/**
	* Destructor. No destruye en ningun caso el manejador de eventos ni 
	* el cliente de metainformacion.
	*/
	~MIServiceConnector();
	
	/**
	* Intenta loguearse en el sistema. Si se permite el login, entonces se admitira
	* llamar al metodo "connect_user" convenientemente.
	*
	* @return Metainformacion de la conexion. Si ocurrio algun error, entonces
	*         el campo error_message sera distinto a NULL y contendra el mensaje
	*         de error producido por el sistema.
	*/
	collaboration::MIConnection* login();
	
	/**
	* Comprueba si se poseen permisos de administracion
	*
	* @return True si se poseen permisos de administracion o False en caso contrario
	*/
	bool admin();
	
	/**
	* Conecta al usuario en el servicio de metainformacion.
	*
	* @return Rol actual del usuario o NULL si no pudo conectarse
	*
	* @pre Debe llamarse al metodo "login" convenientemente
	* @post Se envia un evento distribuido con los siguientes campos: 
	*		- type = (PortableInteger)MI_EVENT_TYPE  (@see MIEvent.h)
	*       - mi_type = (PortableInteger)LOGIN
	*		- application = (char*)this->application
	*		- user = (char*)this->user
	*		- role = (char*)this->role);
	*/
	char* connect_user();
	
	/**
	* Desconecta al usuario del servicio de metainformacion
	*
	* @post Se envia un evento distribuido con los siguientes campos: 
	*		- type = (PortableInteger)MI_EVENT_TYPE  (@see MIEvent.h) 
	* 		- mi_type = (PortableInteger)DISCONNECT
	*		- application = (char*)this->application
	*		- user = (char*)this->user
	*/
	void disconnect_user();
	
	/**
	* Cambia el rol actual del usuario
	*
	* @param newrole Nuevo rol actual del usuario
	* @return Metainformacion de la conexion. Si hubo algun error, entonces
	*         el campo error_message sera distinto a NULL.
	*
	* @post Se envia un evento distribuido con los siguientes campos:  
	*		- type = (PortableInteger)MI_EVENT_TYPE  (@see MIEvent.h)
	* 		- mi_type = (PortableInteger)USER_ROLE_CHANGED
	*		- application = (char*)this->application
	*		- user = (char*)this->user
	*		- role = (char*)this->role
	*/
	collaboration::MIConnection* change_connected_user_role(const char* newrole);
	
	/**
	* Obtiene una lista con los nombres de los usuarios asociados
	* a un rol especifico
	*
	* @param role_name Rol
	* @return Lista con los nombres de los usuarios
	*/
	collaboration::ListOfUserNames* role_users(const char* role_name);
	
	/**
	* Obtiene una lista con los nombres de los usuarios asociados
	* al rol actual
	*
	* @return Lista con los nombres de los usuarios
	*/
	collaboration::ListOfUserNames* role_users();
	
	/**
	* Obtiene los usuarios del sistema
	*
	* @return Lista con la metainformacion de los usuarios
	*/
	collaboration::ListOfUsers* users_info();
	
	/**
	* Obtiene los roles del sistema
	*
	* @return Lista con la metainformacion de los roles
	*/
	collaboration::ListOfRoles* roles_info();
	
	/**
	* Obtiene la metainformacion del usuario
	*
	* @return Metainformacion del usuario
	*/
	collaboration::MIUser* user_info();
	
	/**
	* Obtiene la metainformacion de un usuario
	* 
	* @param user_name Nombre de usuario
	* @return Metainformacion del usuario
	*/
	collaboration::MIUser* user_info(const char* user_name);
	
	/**
	* Obtiene la metainformacion del rol actual
	*
	* @return Metainformacion del rol actual
	*/
	collaboration::MIRole* role_info();
	
	/**
	* Obtiene la metainformacion de un rol
	*
	* @param role_name Nombre del rol
	* @return Metainformacion del rol
	*/
	collaboration::MIRole* role_info(const char* role_name);
	
	/**
	* Obtiene la metainformacion de los usuarios conectados
	*
	* @return Lista con la metainformacion de los usuarios conectados
	*/
	collaboration::ListOfUsers* connected_users();
	
	/**
	* Obtiene los componentes
	*
	* @return Lista con los nombres de los componentes
	*/
	collaboration::ListOfComponentNames* components();
	
	/**
	* Obtiene la metainformacion de los roles permitidos
	*
	* @return Metainformacion de los roles permitidos
	*/
	collaboration::ListOfRoles* allowed_roles();

	/**
	* Obtiene la metainformacion de los roles permitidos para
	* un usuario concreto.
	*
	* @param user_name Nombre del usuario
	* @return Metainformacion de los roles permitidos
	*/
	collaboration::ListOfRoles* allowed_roles(const char* user_name);
	
	/**
	* Asigna un nivel de permisos para un usuario y un componente
	*
	* @param user_name Nombre del usuario
	* @param comp Nombre del componente
	* @param level Nivel de permisos
	* @return True si se pudo asignar el nivel de permisos. False en
	*         caso contrario.
	*
	* @post Se envia un evento distribuido con los siguientes campos:  
	*		- type = (PortableInteger)MI_EVENT_TYPE  (@see MIEvent.h)
	*		- mi_type = (PortableInteger)USER_COMPONENT_PERMISSIONS_CHANGE
	*		- application = (char*)this->application
	*		- user = (char*)user_name
	*		- component = (char*)comp
	*/
	bool set_user_component_permissions(const char* user_name, const char* comp, int level);
	
	/**
	* Asigna un nivel de permisos para el usuario actual y un componente
	*
	* @param comp Nombre del componente
	* @param level Nivel de permisos
	* @return True si se pudo asignar el nivel de permisos. False en
	*         caso contrario.
	*
	* @post Se envia un evento distribuido con los siguientes campos:  
	*		- type = (PortableInteger)MI_EVENT_TYPE  (@see MIEvent.h)
	*		- mi_type = (PortableInteger)USER_COMPONENT_PERMISSIONS_CHANGE
	*		- application = (char*)this->application
	*		- user = (char*)this->user
	*		- component = (char*)comp
	*/
	bool set_user_component_permissions(const char* comp, int level);
	
	/**
	* Asigna un nivel de permisos para un rol y un componente
	*
	* @param role_name Nombre del rol
	* @param comp Nombre del componente
	* @param level Nivel de permisos
	* @return True si se pudo asignar el nivel de permisos. False en
	*         caso contrario.
	*
	* @post Se envia un evento distribuido con los siguientes campos:  
	*		- type = (PortableInteger)MI_EVENT_TYPE  (@see MIEvent.h)
	*		- mi_type = (PortableInteger)ROLE_COMPONENT_PERMISSIONS_CHANGE
	*		- application = (char*)this->application
	*		- role = (char*)role_name
	*		- component = (char*)comp
	*/
	bool set_role_component_permissions(const char* role_name, const char* comp, int level);
	
	/**
	* Asigna un nivel de permisos para el rol actual y un componente
	*
	* @param comp Nombre del componente
	* @param level Nivel de permisos
	* @return True si se pudo asignar el nivel de permisos. False en
	*         caso contrario.
	*
	* @post Se envia un evento distribuido con los siguientes campos:  
	*		- type = (PortableInteger)MI_EVENT_TYPE  (@see MIEvent.h)
	*		- mi_type = (PortableInteger)ROLE_COMPONENT_PERMISSIONS_CHANGE
	*		- application = (char*)this->application
	*		- role = (char*)this->role
	*		- component = (char*)comp
	*/
	bool set_role_component_permissions(const char* comp, int level);
	
	/**
	* Obtiene el nivel de permisos para un componente y el usuario actual
	*
	* @param comp Nombre del componente
	* @return Nivel de permisos
	*/
	PortableInteger get_user_component_permissions(const char* comp);

	/**
	* Obtiene el nivel de permisos para un componente y un usuario
	*
	* @param user_name Nombre del usuario
	* @param comp Nombre del componente
	* @return Nivel de permisos
	*/
	PortableInteger get_user_component_permissions(const char* user_name, const char* comp);	

	/**
	* Obtiene el nivel de permisos para un componente y el rol actual
	*
	* @param comp Nombre del componente
	* @return Nivel de permisos
	*/
	PortableInteger get_role_component_permissions(const char* comp);

	/**
	* Obtiene el nivel de permisos para un componente y un rol
	*
	* @param role_name Nombre del rol
	* @param comp Nombre del componente
	* @return Nivel de permisos
	*/
	PortableInteger get_role_component_permissions(const char* role_name, const char* comp);

	/**
	* Crea un nuevo usuario para el sistema
	*
	* @param user_name Nombre de usuario
	* @param pass Clave
	* @param default_role Rol por defecto para el usuario
	* @param is_admin Indica si es un administrador o no
	* @return True si pudo crearse el usuario. False en caso contrario.
	*
	* @post Se envia un evento distribuido con los siguientes campos:  
	*		- type = (PortableInteger)MI_EVENT_TYPE  (@see MIEvent.h)
	*		- mi_type = (PortableInteger)NEW_USER
	*		- application = (char*)this->application
	*		- user = (char*)user_name
	*/
	bool new_user(const char* user_name, const char* pass, 
							const char* default_role, bool is_admin);

	/**
	* Crea un nuevo rol para el sistema
	*
	* @param role_name Nombre del rol
	* @return True si pudo crearse el rol. False en caso contrario.
	*
	* @post Se envia un evento distribuido con los siguientes campos:  
	*		- type = (PortableInteger)MI_EVENT_TYPE  (@see MIEvent.h)
	*		- mi_type = (PortableInteger)NEW_ROLE
	*		- application = (char*)this->application
	*		- role = (char*)role_name
	*/
	bool new_role(const char* role_name);

	/**
	* Crea un nuevo rol permitido para un usuario
	*
	* @param user_name Nombre del usuario
	* @param role_name Nombre del nuevo rol permitido
	* @return True si pudo crearse el nuevo rol permitido. False
	*         en caso contrario.
	*
	* @post Se envia un evento distribuido con los siguientes campos:  
	*		- type = (PortableInteger)MI_EVENT_TYPE  (@see MIEvent.h)
	*		- mi_type = (PortableInteger)NEW_ALLOWED_ROLE
	*		- application = (char*)this->application
	*		- user = (char*)user_name
	*		- role = (char*)role_name
	*/
	bool new_user_allowed_role(const char* user_name, const char* role_name);
	
	/**
	* Crea un nuevo rol permitido para el usuario
	*
	* @param role_name Nombre del nuevo rol permitido
	* @return True si pudo crearse el nuevo rol permitido. False
	*         en caso contrario.
	*
	* @post Se envia un evento distribuido con los siguientes campos:  
	*		- type = (PortableInteger)MI_EVENT_TYPE  (@see MIEvent.h)
	*		- mi_type = (PortableInteger)NEW_ALLOWED_ROLE
	*		- application = (char*)this->application
	*		- user = (char*)this->user
	*		- role = (char*)role_name
	*/
	bool new_user_allowed_role(const char* role_name);

	/**
	* Elimina un rol permitido para un usuario
	*
	* @param user_name Nombre del usuario
	* @param role_name Nombre del rol a eliminar
	* @return True si pudo eliminarse el rol permitido. False
	*         en caso contrario.
	*
	* @post Se envia un evento distribuido con los siguientes campos:  
	*		- type = (PortableInteger)MI_EVENT_TYPE  (@see MIEvent.h)
	*		- mi_type = (PortableInteger)REMOVE_ALLOWED_ROLE
	*		- application = (char*)this->application
	*		- user = (char*)user_name
	*		- role = (char*)role_name
	*/
	bool remove_allowed_user_role(const char* user_name, const char* role_name);
	
	/**
	* Elimina un rol permitido para el usuario
	*
	* @param role_name Nombre del rol a eliminar
	* @return True si pudo eliminarse el rol permitido. False
	*         en caso contrario.
	*
	* @post Se envia un evento distribuido con los siguientes campos:  
	*		- type = (PortableInteger)MI_EVENT_TYPE  (@see MIEvent.h)
	*		- mi_type = (PortableInteger)REMOVE_ALLOWED_ROLE
	*		- application = (char*)this->application
	*		- user = (char*)this->user
	*		- role = (char*)role_name
	*/
	bool remove_allowed_user_role(const char* role_name);
	
	/**
	* Elimina un usuario del sistema
	*
	* @param user_name Nombre del usuario
	* @return True si se pudo eliminar el usuario. False en caso contrario.
	*
	* @post Se envia un evento distribuido con los siguientes campos:  
	*		- type = (PortableInteger)MI_EVENT_TYPE  (@see MIEvent.h)
	*		- mi_type = (PortableInteger)REMOVE_USER
	*		- application = (char*)this->application
	*		- user = (char*)user_name
	*/
	bool remove_user(const char* user_name);

	/**
	* Elimina un rol del sistema
	*
	* @param role_name Nombre del rol
	* @return True si se pudo eliminar el rol. False en caso contrario.
	*
	* @post Se envia un evento distribuido con los siguientes campos: 
	*		- type = (PortableInteger)MI_EVENT_TYPE  (@see MIEvent.h)
	*		- mi_type = (PortableInteger)REMOVE_ROLE
	*		- application = (char*)this->application
	*		- role = (char*)role_name
	*/
	bool remove_role(const char* role_name);
	
	/**
	* Obtiene el rol actual de un usuario
	*
	* @param user_name Nombre del usuario
	* @return Rol actual del usuario
	*/
	char* user_actual_role(const char* user_name);
	
	/**
	* Obtiene el rol actual del usuario
	*
	* @return Rol actual del usuario
	*/
	char* user_actual_role();

	/**
	* Guarda la metainformacion del sistema de forma persistente
	*/
	void commit();
};

#endif
